package com.ztesoft.report.base.controller;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ztesoft.report.base.pojo.R;
import com.ztesoft.report.entity.ReportDto;
import com.ztesoft.report.entity.TempUploadLog;
import com.ztesoft.report.exception.RException;
import com.ztesoft.report.service.DataService;
import com.ztesoft.report.service.ReportService;
import com.ztesoft.report.service.TempUploadLogService;
import com.ztesoft.report.service.UserService;
import com.ztesoft.report.utils.ExcelDataExport;
import com.ztesoft.report.utils.excel.ExcelUtils;
import com.ztesoft.report.utils.excel.FileUtils;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.util.*;

/**
 * Controller基类
 *
 * @author y3tu
 */
public class BaseController {

    @Autowired
    private UserService UserService;

    @Autowired
    DataService dataService;
    @Autowired
    ReportService reportService;
    @Autowired
    FileUtils fileUtils;
    @Autowired
    TempUploadLogService tempUploadLogService;

    @Value("${com.ztesoft.createExcelPath}")
    private String createExcelPath;
    @Value("${com.ztesoft.downpath}")
    private String downLoadPath;
    //分页插入Excel的大小
    @Value("${com.ztesoft.intoExcelOnceRows}")
    private Integer pageSize;



    /**
     * 重定向至地址 url
     *
     * @param url 请求地址
     * @return
     */
    protected String redirectTo(String url) {
        StringBuffer rto = new StringBuffer("redirect:");
        rto.append(url);
        return rto.toString();
    }

    protected Object filterNull(Map map, String key) {
        return map.get(key) == null ? "" : map.get(key);
    }

    /**
     * 根据url判断该用户是否拥有该页面权限
     * @param
     * @param href
     * @return true  拥有；false  无
     */
    public boolean isPressionMenu(String staffId, String operateTime, String gsReportTradeId,String signmsg,String href){
        Map map=new HashMap(2);
        map.put("userId",staffId);
        map.put("href",href);

        String isPression= UserService.getUserPerms(map);
        if (isPression!=null&&isPression!=""){
            return true;
        }else {
            return false;
        }
    }

    /**
     * 根据权限编码判断该用户是否拥有按钮权限
     * @param userId
     * @param permCode
     * @return
     */
    public boolean isPressionButton(String userId,String permCode){
        Map map=new HashMap();
        map.put("userId",userId);
        map.put("permCode",permCode);
        String PressionButtonCode= UserService.getUserpermCode(map);

        if (PressionButtonCode!=null&&PressionButtonCode!=""){
            return true;
        }else {
            return  false;
        }
    }

    public static String getCurrentUser() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpSession session=request.getSession();
        Map map= (Map) session.getAttribute("map");
        if (session.getAttribute("map")!=null){
            String userId=(String) map.get("userId");
            if (map.get("userId")==null){
                return "1";
            }else {
                return userId;
            }
        }else {
            return "1";
        }
    }

    public String TokenIdReturnUser(String tokenId){
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpSession session=request.getSession();
        session.setAttribute("admin",tokenId);
        return tokenId;
    }



    /**
     * 生成Excel
     *
     * @param reportDto       报表对象
     * @param createExcelPath 创建路径
     * @param fileName        文件名
     */
    public Integer createExcel(ReportDto reportDto, String createExcelPath, String fileName,String yyyyMMdd) throws Exception {
        String sheetName = reportDto.getName();
        File file = new File(createExcelPath);
        if (!file.exists()) {
            file.mkdirs();
        }
        String filePath = createExcelPath + "/" + fileName;
        List<String> headConfig = new ArrayList<>();
        String header = reportDto.getHeader();
        //meta是下面用来处理是否导出字段时候使用
        String meta = reportDto.getMetaColumns();
        JSONArray jsonArrayMeta = JSON.parseArray(meta);
        //创建一个集合，用来放所有字段的json串
        List<String> metaList = new ArrayList<>();
        for (Object obj:jsonArrayMeta) {
            metaList.add(obj.toString());
        }
        //使用此方法得到导出EXCEL时不需要使用的字段
        List<String> cancelExport = this.getCancelExcel(metaList);
        JSONArray jsonArray = JSON.parseArray(header);
        //判断如果有不需要使用的串，此集合必定大小不为0
        if (cancelExport == null || cancelExport.size() == 0) {
            for (Object obj : jsonArray) {
                headConfig.add(obj.toString());
            }
        } else {
            for (Object obj : jsonArray) {
                boolean flag = true;
                //头串是用"分公司=={\"width\":\"12\",\"field\":\"A\"}" 等号 == 隔开，所以要先分出来
                String[] objStr = obj.toString().split("==");
                //把等号后面变成json对象，方便取出对应的字段，与前面我们不需要导出的字段做对比，
                //如果在不需要导出集合中找不到，那就正常放在headConfig里面
                JSONObject j = JSONObject.parseObject(objStr[1]);
                for (String cancel:cancelExport) {
                    if (cancel.toUpperCase().equals(j.get("field").toString().toUpperCase())){
                        flag = false;
                        break;
                    }
                }
                if (flag) {
                    headConfig.add(obj.toString());
                }
            }
        }
        int listCount = dataService.executeQuerySqlCount(reportDto.getDsId().toString(), reportDto.getSqlText());
        if (listCount > 0) {
            //生成表头
            R r = fileUtils.createExcelByPOIKit(fileName, sheetName, listCount, Map.class, filePath, headConfig,reportDto.getHeadTemp(),reportDto.getEndTemp(),yyyyMMdd);
            ExcelDataExport excelDataExport = (ExcelDataExport) r.get("data");
            //生成表体
            ExcelUtils excelUtils = excelDataExport.getExcelUtils();
            int rowNum = 0;
            for (int i = 0; ; i++) {
                List<Map> maps = dataService.executeQuerySqlByPage(reportDto.getDsId().toString(),
                        reportDto.getSqlText(), pageSize, i + 1);
                if (maps.size() < 1) {
                    break;
                }
                //通过sql查询数据返回map
                //将值转换
                reportService.valueParseByConvertType(reportDto, maps);
                excelUtils.addExcelRow(maps, excelDataExport.getExportItems(),
                        excelDataExport.getWorkbook(), rowNum, excelDataExport.getRow(), 0, 100000);
                rowNum += maps.size();
            }
            ExcelUtils.createExcel(excelDataExport.getmResponse(), excelDataExport.getHandler(),
                    excelDataExport.getSheetName(), excelDataExport.getWorkbook(), excelDataExport.getOut());
            return excelDataExport.getRow();
        } else {
            throw new RException("该报表无数据无法生成Excel");
        }
    }

    /**
     * 传入所有字段集合，返回不需要导出的字段
     * @param metaList
     * @return
     */
    public List<String> getCancelExcel(List<String> metaList) {
        List<String> cancelExcel = new ArrayList<>();
        for (String str:metaList) {
            JSONObject j = JSONObject.parseObject(str);
            String isExport = j.get("isExport").toString();
            if ("false".equals(isExport)) {
                cancelExcel.add(j.get("name").toString());
            }
        }
        return cancelExcel;
    }


    /**
     * 在生成好的Excel里面添加模板
     *
     * @param createExcelPath
     * @param fileName
     * @throws Exception
     */
    public void hanldeExcel(ReportDto reportDto, String createExcelPath, String fileName, Integer rowNum) throws Exception {
        //解析出需要合并的列
        String header = reportDto.getMetaColumns();
        List<Map> headConfig = new LinkedList<>();
        List<Integer> cellNums = new ArrayList<>();
        JSONArray jsonArray = JSON.parseArray(header);
        for (Object obj : jsonArray) {
            Map isMergeCell = JSON.parseObject(obj.toString());
            headConfig.add(isMergeCell);
        }
        //这里有一个问题，比如有一个不需要导出的列BILL_MONTH,isExport会是False，因此在我们展示的列本来比如有
        //10列，隐藏一行就只有9列，这里如果我们要合并的列是最后3列，合并的列号自然前是7、8、9，
        // 并且BILL_month这个字段处于中间，就会造成我们实际只有9列本来需要合并的最后3列列号是6、7、8，所以要得到合并
        //的列号之前，要分析isExport是否可导出的列的位置，下面写个小算法，逻辑是
        //初始化一个k为0，并分析每列的isExport是不是有为false的，如果有则k+1，最后在计算列号的时候把k值减去就行了
        int k = 0;
        for (int i = 0; i < headConfig.size(); i++) {
            if ("false".equals(headConfig.get(i).get("isExport"))) {
                k++;
            }
            if ("true".equals(headConfig.get(i).get("isMergeCell"))) {
                //把对应的列号存入,从0开始,不需要加1,也是为了方便后面合并
                cellNums.add(i-k);
            }
        }
        System.out.println("需要合并的列号是（第一列为0）："+cellNums);
        //查询出尾部模板的路径，下面用于读取
        TempUploadLog tempUploadLog = tempUploadLogService.selectById(reportDto.getEndTemp());
        FileInputStream inStream = null;
        FileOutputStream os = null;
        try {
            //已经生成的EXCEL
            inStream = new FileInputStream(new File(createExcelPath));
            //尾部模板EXCEL
            //InputStream inputStream = new ClassPathResource("excel/endtemp.xls").getInputStream();
            InputStream inputStream = new FileInputStream(new File(downLoadPath+"/"+tempUploadLog.getTempName()));

            //读取尾部模板
            Workbook endwb = WorkbookFactory.create(inputStream);
            Sheet endSheet = endwb.getSheetAt(0);
            Workbook wb = WorkbookFactory.create(inStream);
            int sheetCount = wb.getNumberOfSheets();
            for (int i = 0; i < sheetCount; i++) {
                Sheet sheet = wb.getSheetAt(i);
                //getLastRowNum()此方法如果最后一行是5，获取的数字是4
                Integer lastRowNum = sheet.getLastRowNum();
                if (cellNums != null && cellNums.size() > 0) {
                    //遍历列号的集合 取出需要合并的列号
                    for (Integer cellNum : cellNums) {
                        //这个就是合并单元格
                        //参数说明：1：开始行 2：结束行  3：开始列 4：结束列
                        //比如我要合并 第二行到第四行的    第六列到第八列     sheet.addMergedRegion(new CellRangeAddress(1,3,5,7));
                        System.out.println("-----正在合并列："+cellNum);
                        sheet.addMergedRegion(new CellRangeAddress(rowNum, lastRowNum - 1, cellNum, cellNum));
                        System.out.println(cellNum+"列合并完成");
                    }
                }
                //获取最后一行并空2行开始
                for (Row endrow : endSheet) {
                    boolean nullFlag = true;
                    for (Cell c : endrow) {
                        if (c != null && c.toString() != "") {
                            nullFlag = false;
                        }
                    }
                    if (nullFlag) {
                        continue;
                    }
                    Row row = sheet.createRow(sheet.getLastRowNum() + 3);
                    ExcelUtils.copyRow((HSSFWorkbook) wb, (HSSFSheet) sheet, (HSSFSheet) endSheet,(HSSFRow) endrow, (HSSFRow) row, true);
                }
                os = new FileOutputStream(createExcelPath);
                wb.write(os);
                //删除尾部模板
                File endFile = new File(downLoadPath+"/"+tempUploadLog.getTempName());
                if (endFile.exists()) {
                    endFile.delete();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RException(e.getMessage());
        } finally {
            try {
                if (inStream != null) {
                    inStream.close();
                }
                if (os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}
